/**
 * CS307 Team 11 Spring 2011
 * @author Kevin Barlow
 * @author David Barnes
 * @author Max Cohen
 * @author Yoongu Kang
 * @author Matt Lathrop
 * @author Thomas Maley
 * 
 * 
 * This class is used for parsing through the recurrence relation 
 * or just used as a test to show how to parse the recurrence relation
 */

package com.cs307.redcross.server;

import java.util.*;

import com.cs307.redcross.client.*;

@SuppressWarnings("deprecation")
public class Recurrence {

	/*
	 * Thought for recurrence
	 * 
	 * 				D | W  	    		   M							 Y	
	 * [D|W|M|Y] : [MTWHFSU] | [DOM [0-31] | DOW [0-4] ; [M|T|W|H|F]] | [Date]:[0-9]*
	 * 
	 * First parameter:
	 * 
	 * 		  D:
	 * 			Daily
	 *		  W:
	 *			Weekly
	 *		  M:
	 *			Monthly
	 *		  Y:
	 *			Yearly
	 * 
	 * Second parameter:
	 * 
	 * 		Daily: 
	 * 			The Event will happen everyday
	 *	 
	 * 		Weekly:
	 * 			 Takes a list of days to occur on instead of just one
	 * 		   
	 * 		Monthly:
	 * 			DOM: Day at which the occurrence should happen
	 * 			DOW: The number it will occur then the day it will occur (example: 2nd Saturday )
	 * 
	 * 		Yearly: 
	 * 			The date it should occur on
	 * 
	 * Third parameter: 
	 * 		number of occurrences
	 *
	 *
	 * Possible errors:
	 * 		-What happens if the user asks for the 4th Friday of each month and that Friday doesn't occur in that month
	 * 			Solved: just ignore that Friday
	 * 
	 * 		-The recurrence sting will have to be well formed
	 * 
	 * 		-May need to implement
	 *
	 */
	
	boolean Monday;
	boolean Tuesday;
	boolean Wednesday;
	boolean Thursday;
	boolean Friday;
	boolean Saturday;
	boolean Sunday;
	
	boolean Daily;
	boolean Weekly;
	boolean Monthly;
	boolean Yearly;
	
	boolean DOM;
	boolean DOW;
	
	int amount;
	int day;
	
	int year_offset;
	int day_offset;
	int week_offset;
	
	int week_pointer;
	
	Date endDate;
	
	Calendar cal;
	
	String rec;
	String date;
	String recurrenceString;
	
	Event aEvent;
	
	/**
	 * Takes an Event and sets up the class to set up recurrences
	 * 
	 * @param e
	 */
	public Recurrence(){
		
	}
	public Recurrence( Event e ){
		this( e, e.getRecurrence() );
	}
	/**
	 * takes an event and a recurrence relation
	 * @param e
	 * @param recurrence
	 */
	public Recurrence( Event e, String recurrence ){
		
		init( e, recurrence );
	
	}
	/**
	 * Parses the recurrence string and sets the appropriate variables
	 * @param rec
	 */
	private void run( String rec ){
	
		StringTokenizer st = new StringTokenizer( rec, ":" );
	
		String first = st.nextToken().toUpperCase();
			
		if ( first.equals( "D") ){
			
			////System.out.println( "Daily");
				
			Daily = true;
			st.nextToken().toUpperCase();
			
			//String third = st.nextToken().toUpperCase();
			//recurrenceString = "Will happen for " + third + " day(s)";
			////System.out.println( "Will happen " + third + " day(s)");
			//amount = Integer.parseInt( third );
				
		}else if ( first.equals( "W" ) ){
				
			////System.out.println( "Weekly");
			Weekly = true;	
			
			String second = st.nextToken().toUpperCase();
			recurrenceString= "Will happen ";
			for ( char c : second.toCharArray() ){					
				
				String day = getDay( Character.toString( c ) );
				recurrenceString+= day +" ";
			}
				
			//String third = st.nextToken();
			//amount = Integer.parseInt( third );
			//recurrenceString+= "for " + third +" week(s)" ;
			
		
		}else if ( first.equals( "M" ) ){
				
			////System.out.println( "Monthly" );
			Monthly = true;
			
			String second = st.nextToken();
			StringTokenizer sts = new StringTokenizer( second, ";");
				
			if ( sts.countTokens() == 1 ){
				String d = sts.nextToken();
				////System.out.println( "Will occur on the " + d + " of every Month");
			
				recurrenceString = "Will occur on the " + d + " of every Month";
				DOM = true;
				day = Integer.parseInt( d );
				
			}else if ( sts.countTokens() == 2 ){
					
				DOW = true;
				day = Integer.parseInt( sts.nextToken() );
				String g = getDay( sts.nextToken() );
				//System.out.println( g );
				recurrenceString ="Will occur on the " + day + " " + g + " of every month";
					
			}
			
			//String third = st.nextToken();
			////System.out.println( "Will happen " + third + " time(s)");
			//recurrenceString+= " " + third + " time(s)";
			//amount = Integer.parseInt( third );
				
		}else if ( first.equals( "Y") ){
				
			////System.out.println( "Yearly" );
			Yearly = true;
				
			String second = st.nextToken();
			////System.out.println( "Will happen yearly on " + second );
			date = second;
				
			//String third = st.nextToken();
			////System.out.println( "Will happen " + third + " year(s)");
			//amount = Integer.parseInt( third );
			recurrenceString = "Will happen yearly on " + second;
				
		}else{
			//something bad happened
			//need to do error checking
			//may need to throw an error
			
		}
	}
	/**
	 * gets the next day for the recurrence relation
	 * @return the next day in the recurrence pattern
	 */
	//@SuppressWarnings("deprecation")
	public Date getNextDate(){
		/*
		 * Sunday = 1
		 * Monday = 2
		 * Tuesday = 3
		 * Wednesday = 4
		 * Thursday = 5
		 * Friday = 6
		 * Saturday = 7
		 */
				
		Date d = null;
		if ( Daily ){
			////amount--;
			
			cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH)+1 );
			
			return cal.getTime();
		}
		if ( Weekly ){
			
			while( true ){
				
				int dayOfWeek = cal.get( Calendar.DAY_OF_WEEK );
				if ( Sunday  && dayOfWeek == 1 ){
					
					////System.out.println( "Sunday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
					
					
				}else if ( Monday && dayOfWeek == 2 ){
					
					////System.out.println( "Monday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
					
				}else if ( Tuesday && dayOfWeek == 3 ){
					
					////System.out.println( "Tuesday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
				
				}else if ( Wednesday && dayOfWeek == 4 ){
					
					////System.out.println( "Wednesday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
					
				}else if ( Thursday && dayOfWeek == 5 ){
					
					////System.out.println( "Thursday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
					
				}else if ( Friday && dayOfWeek == 6 ){
					
					////System.out.println( "Friday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
					
				}else if ( Saturday && dayOfWeek == 7 ){
					
					////System.out.println( "Saturday" );
					//amount--;
					Date ret = cal.getTime();
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					return ret;
					
				}else{
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
				}
			}
		
		}
		
		if ( Monthly ){
			
			//amount--;
			if ( DOM ){
				
				while( true ){
					
					if ( cal.get(Calendar.DAY_OF_MONTH) == day ){
					
						cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					
						return cal.getTime();
					
					}
					
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );

				}
				
			}
			
			if ( DOW ){
				
				int week_num = cal.get( Calendar.WEEK_OF_MONTH );				
				
				while( true ){
					
					if ( week_num == day ){
						
						if ( Sunday ){
							
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
						
							return ret;
						
						}else if ( Monday && cal.get( Calendar.DAY_OF_WEEK ) == 2 ){
							 
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
						
							return ret;
						
						}else if( Tuesday ){
							
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
						
							return ret;
						
						}else if ( Wednesday ){
						
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
							
							return ret;
						
						}else if ( Thursday ){
							
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
						
							return ret;
						
						}else if ( Friday ){
							
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
						
							return ret;
						
						}else if ( Saturday ){
						
							Date ret = cal.getTime();
							cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
						
							return ret;
						
						}
					}
					cal.set( Calendar.DAY_OF_MONTH, cal.get( Calendar.DAY_OF_MONTH ) + 1 );
					week_num = cal.get( Calendar.DAY_OF_WEEK_IN_MONTH );
				
				}
			}
			return d;
		}
		
		if ( Yearly ){
			
			
			StringTokenizer st = new StringTokenizer( date, "/" );
			int month = Integer.parseInt( st.nextToken() ) - 1;
			int day = Integer.parseInt( st.nextToken() );
			
			//amount--;
	
			cal.set( Calendar.MONTH, month );
			cal.set( Calendar.DAY_OF_MONTH, day );
			
			Calendar c = Calendar.getInstance();
			if ( cal.getTime().after( c.getTime() ) ){
					
					Date ret = cal.getTime();
					cal.roll( Calendar.YEAR, 1 );
					return ret;

				
			}else{
				cal.roll( Calendar.YEAR, 1 );
				Date ret = cal.getTime();
				cal.roll( Calendar.YEAR, 1 );
				return ret;
				
			}
		}
		
		return null;
		
	}
	
	/**
	 * shows the number of days left
	 * @return the number of days left to recover
	 */
	public int getDatesLeft(){
	
		return amount;
	
	}
	/**
	 * Gets all the dates for the recurrences
	 * 
	 * @return a Vector of all the dates
	 */
	private Vector<Date> getAllDates(){
		
		Vector<Date> dates = new Vector<Date>();
		while( amount-- > 0 ){
			Date d = this.getNextDate();
			dates.add( d );
		
		}
		
		return dates;
	}
	private void init( Event e, String recurrence ){
		Monday = Tuesday = Wednesday = Thursday = Friday = Saturday = Sunday = false;
		Daily = Weekly = Yearly = false;
		//amount = day = 0;
		day = 0;
		date = "";
		year_offset = 1;
		day_offset = 0;
		week_pointer = 0;
		week_offset = 0;
		
		cal = Calendar.getInstance();
		cal.setTime( e.getStartTime() );
		
		DOM = false;
		DOW = false;
		rec = recurrence;
		aEvent = e;
		
		run( recurrence );
	}
	
	/*
	 * The way to implement a new recurrence is set up like this
	 * the first two parameters are the same as before but the user can pass a date or a number
	 * and we will work from there 
	 * 
	 */
	
	//End date now gets put into the pattern instead of number of recurrences
	//	best way to do this is calculate the number of recurrences till the end date
	
			//JUST HAVE TO CHANGE setAllEvents to have different parameters
	//TODO: take a pattern and a number of recurrences and calculates the end date
	//TODO: takes a pattern and an end date and calculates the number of recurrences left
	
	
	
	
	/**
	 * Takes and event and an end date and creates all the events in the recurrence and writes them to the database
	 * @return the number of events left in the recurrence pattern
	 */
	
	public int numberOfEvents( Event aEvent, Date d ){
		
		Vector<Event> events = new Vector<Event>();
		Event tmp;
		amount = 10000;
		init( aEvent, aEvent.getRecurrence() );
		tmp = aEvent;
		Date dtmp = tmp.getStartTime();
		while( dtmp.before( d ) ){
			
			dtmp = this.getNextDate();		
			int next_id = 0;
			
			String n = aEvent.getName();
			String des  = aEvent.getDescription();
			String re = aEvent.getRecurrence();
			Vector<Group> g = aEvent.getGroups();
			Vector<Person> p = aEvent.getPeople();
			
			Date start = dtmp;
			Date tend = aEvent.getEndTime();
			Date end = new Date( dtmp.getYear(), dtmp.getMonth(), dtmp.getDate(), tend.getHours(), tend.getMinutes() );
			Event e = new Event( n, des, start, end, re, next_id, g, p );
			events.add( e );

		}
		
		return events.size();
	}
	
	/**
	 * Takes and event and a number of recurrences and creates all the events in the recurrence and writes them to the database
	 * @return the number of events left in the recurrence pattern
	 */
	public Date endDateOfEvents( Event aEvent, int num ){
		
		this.init( aEvent, aEvent.getRecurrence() );
		amount = num;
		Vector<Event> e = this.setAllEvents( aEvent );
		
		return e.get(e.size()-1).getStartTime();
		
		//return d;
	}
	/**
	 * Takes an Event and figures out how many are left
	 * @param aEvent
	 * @return the number of remaining events
	 */
	public int remainingEvents( Event aEvent ){
		int num = 0;
		
		try{
			DatabaseHandler db = DatabaseHandler.create();
		while( aEvent.getNextEventID() != -1 ){
			aEvent = db.getEvent( aEvent.getNextEventID() );
			num++;
		}
		
		}catch ( Exception e ){
			System.err.println( e.getMessage() );
		}
		return num;
	}
	
	
	/**
	 * gets all the Events based on the recurrence pattern
	 * @return a Vector of all the events created
	 */
	public Vector<Event> setAllEvents( Event aEvent ){

		
		Vector<Event> events = new Vector<Event>();
		events.add( aEvent );
		Vector<Date> dates = this.getAllDates();
	
		
		int next_id = 0;
		String n = aEvent.getName();
		String d  = aEvent.getDescription();
		String re = aEvent.getRecurrence();
		Vector<Group> g = aEvent.getGroups();
		Vector<Person> p = aEvent.getPeople();
		
		for (Date dt : dates){
			Date start = dt;
		
			Date tend = aEvent.getEndTime();
			Date end = new Date( dt.getYear(), dt.getMonth(), dt.getDate(), tend.getHours(), tend.getMinutes() );
			Event e = new Event( n, d, start, end, re, next_id, g, p );
			events.add( e );
		}
		
		writeEvents( events );
		return events;
		
	}
	/**
	 * Writes all the Events to the database
	 * @param ee
	 */
	private void writeEvents( Vector<Event> ee ){
		
		int end = ee.size() - 1;
		int next_id = -1;
		
		try{
			
			DatabaseHandler db = DatabaseHandler.create();
			
			while( end > 0 ){
			
				Event tevent = ee.get( end );
				tevent.setNextEventID( next_id );
				next_id = db.write( tevent );
			
				end--;
			
			}
		
		}catch( Exception e ){
			e.printStackTrace();
		}
		
	}
	
	/**
	 * Deletes an event from the database and sets the next event for the event before the deleted Event
	 * to the event after the deleted event
	 * @param e
	 */
	public static void deleteEvent( Event e ){

		try{
			DatabaseHandler db = DatabaseHandler.create();
			Vector<Event> events = db.getAllEvents();
		
			for( Event ev : events ){
				
				if ( ev.getNextEventID() == e.getId() ){
					db.delete( e );
					ev.setNextEventID( e.getNextEventID() );
					db.write( ev );
				}
				
			}
			
		}catch( Exception exc ){
			
		}
	}
	/**
	 * gets the day based on the character for that date
	 * @param d
	 * @return the String of the day
	 */
	private String getDay( String d ){
		
		d = d.toUpperCase();
		
		if ( d.equals( "M" ) ){
		
			Monday = true;
			return "Monday";
		
		}else if ( d.equals( "T" ) ){
		
			Tuesday = true;
			return"Tuesday";
		
		}else if ( d.equals( "W" ) ){
		
			Wednesday = true;
			return "Wednesday";
		
		}else if ( d.equals( "H" ) ){
		
			Thursday = true;
			return "Thursday";
		
		}else if ( d.equals( "F" ) ){
		
			Friday = true;
			return "Friday";
		
		}else if ( d.equals( "S" ) ){
		
			Saturday = true;
			return "Saturday";
		
		}else if ( d.equals( "U" ) ){
		
			Sunday = true;
			return "Sunday";
		
		}
		//need to do error checking
		//may need to throw error
		return "error";
	}
	/**
	 * returns the recurrence pattern
	 * @return
	 */
	public String getRecurrence(){
		return rec;
	}

	/**
	 * Makes the recurrence string better to understand
	 */
	public String toString(){
		
		return recurrenceString;
	}
	
	/**
	 * Prints all the events in the database
	 */
	private static void printEvents(){
		try{
			DatabaseHandler db = DatabaseHandler.create();
			Vector<Event> events = db.getAllEvents();
			for ( Event et: events ){
				System.out.println( et );
			}
		}catch( Exception e ){
			System.out.println( e.getMessage() );
			
		}
	}
	/**
	 * update future events based on recurrences( deletes old events) and start or end times
	 * @param aEvent
	 */
	public void updateFutureEvents( Event aEvent ){
		try{
			DatabaseHandler db = DatabaseHandler.create();
			Event last = aEvent;
		
			Event next = db.getEvent( aEvent.getId() );
			System.out.println();
			
			while( next.getNextEventID() != -1 ){
				db.delete( next );
				next = db.getEvent( next.getNextEventID() );
			
			}
		db.delete( next );
		db.delete(last);
		}catch ( Exception e ){}
		this.setAllEvents( aEvent );
	}
	
	public static void main( String[] args ) throws Exception{
		
		System.out.println( "Recurrence" );
		//public Event(String n, String d, Date s, Date e, String r, int next, Vector<Group> g, Vector<Person> p)
		Event seed = new Event( "name", "this", new Date( 2011-1900, 01, 20, 10, 30), new Date( 2011-1900, 12, 20, 11, 30), "D:D",3, new Vector<Group>(), new Vector<Person>() );
		Recurrence r = new Recurrence(  );
		r.init( seed, seed.getRecurrence() );
		//printEvents();
		r.numberOfEvents( seed, new Date( 2011-1900, 2, 20 ) );
		//Date d = r.endDateOfEvents( seed, -1 );
		printEvents();
		//System.out.println( d );
	}

}
